/*!
\copyright  Copyright (c) 2020 - 2025 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\brief      Implementation of common LE Audio specifc testing functions.
*/

#include "le_audio_test.h"
#include <logging.h>

#include "focus_audio_source.h"
#include "focus_voice_source.h"
#include "le_advertising_manager_select_extended.h"
#ifdef INCLUDE_LE_AUDIO_BROADCAST
#include <gatt.h>
#include "le_broadcast_manager.h"
#include "le_broadcast_manager_source.h"
#include "le_audio_messages.h"
#include "le_broadcast_music_param.h"
#endif
#if defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN)
#include "le_broadcast_manager_self_scan.h"
#endif
#ifdef INCLUDE_TWS
#include "tws_topology.h"
#endif
#include "volume_messages.h"
#include "volume_service.h"

#include "connection_manager_list.h"
#include "bt_device.h"
#include "gatt_connect.h"
#include "mirror_profile.h"
#include <kymera_chain_config_callbacks.h>


/* Move all functions into this section to ensure they are not removed during
 * garbage collection */

#if defined(INCLUDE_LE_AUDIO_BROADCAST) || defined(INCLUDE_LE_AUDIO_UNICAST)

#ifndef LE_AUDIO_TEST_PA_DATA_MAX_SIZE
#define LE_AUDIO_TEST_PA_DATA_MAX_SIZE      0x08u
#endif

static void leAudioTest_taskHandler(Task task, MessageId id, Message msg);

static TaskData le_audio_test_taskdata = {leAudioTest_taskHandler};

#ifdef INCLUDE_LE_AUDIO_BROADCAST
static uint8 le_audio_test_pa_data_len;
static uint8 le_audio_test_pa_data[LE_AUDIO_TEST_PA_DATA_MAX_SIZE];
#endif /* INCLUDE_LE_AUDIO_BROADCAST */
#endif /* INCLUDE_LE_AUDIO_BROADCAST || INCLUDE_LE_AUDIO_UNICAST */

#ifdef INCLUDE_LE_AUDIO_BROADCAST_SOURCE
#ifndef LE_AUDIO_TEST_PERIODIC_ADV_DATA_SIZE_MAX
#define LE_AUDIO_TEST_PERIODIC_ADV_DATA_SIZE_MAX      (243U)
#endif /* !LE_AUDIO_TEST_PERIODIC_ADV_DATA_SIZE_MAX */

static void leAudioTest_setPATaskHandler(Task task, MessageId id, Message msg);
static TaskData le_audio_test_set_pa_taskdata = {leAudioTest_setPATaskHandler};
uint8 *adv_data = NULL;

/*! Initialize le audio test PA task data */
static leAudioTestSetPaTaskData le_audio_test_pa = {
    .time_to_sync_point_ms = 0,
    .time_pa_anchor_point_us = 0,
    .time_pa_sync_point_us = 0,
};

/*! Get pointer to le audio test PA task data struture. */
#define LeAudioTestPaGetTaskData()      (&le_audio_test_pa)
#endif /* INCLUDE_LE_AUDIO_BROADCAST_SOURCE */

UNITCODESECTION(KEEP)
bool leAudioTest_IsExtendedAdvertisingActive(void)
{
#ifndef INCLUDE_LEGACY_LE_ADVERTISING_MANAGER
    // needs revisiting, maybe remove
    return FALSE;
#else
    return LeAdvertisingManager_IsExtendedAdvertisingActive();
#endif
}

UNITCODESECTION(KEEP)
bool leAudioTest_IsBroadcastReceiveActive(void)
{
#ifdef INCLUDE_LE_AUDIO_BROADCAST
    return LeBroadcastManager_IsBroadcastReceiveActive();
#else
    return FALSE;
#endif
}

UNITCODESECTION(KEEP)
bool leAudioTest_IsAnyBroadcastSourceSyncedToPa(void)
{
#ifdef INCLUDE_LE_AUDIO_BROADCAST
    return LeBroadcastManager_IsAnySourceSyncedToPa();
#else
    return FALSE;
#endif
}

UNITCODESECTION(KEEP)
bool leAudioTest_IsAnyBroadcastSourceSyncedToBis(void)
{
#ifdef INCLUDE_LE_AUDIO_BROADCAST
    return LeBroadcastManager_IsAnySourceSyncedToBis();
#else
    return FALSE;
#endif
}

static bool leAudioTest_IsLeAudioBroadcastAudioInFocus(audio_source_t *focused_source)
{
    if (!Focus_GetAudioSourceForContext(focused_source))
    {
        *focused_source = audio_source_none;
    }

    return *focused_source == audio_source_le_audio_broadcast;
}

static bool leAudioTest_IsLeAudioUnicastAudioInFocus(audio_source_t *focused_source)
{
    if (!Focus_GetAudioSourceForContext(focused_source))
    {
        *focused_source = audio_source_none;
    }

    return AudioSource_IsLeUnicastSource(*focused_source);
}

static bool leAudioTest_IsLeAudioUnicastVoiceInFocus(voice_source_t *focused_source)
{
    if (!Focus_GetVoiceSourceForContext(ui_provider_telephony, focused_source))
    {
        *focused_source = voice_source_none;
    }

    return VoiceSource_IsLeUnicastSource(*focused_source);
}

UNITCODESECTION(KEEP)
static bool leAudioTest_SetVolumeForLeaAudioSource(audio_source_t source, uint8 volume)
{
#ifdef INCLUDE_TWS
    if (TwsTopology_IsRoleSecondary())
    {
        DEBUG_LOG_ALWAYS("This Test API should never be called on the secondary Earbud");
        return FALSE;
    }
#endif

    Volume_SendAudioSourceVolumeUpdateRequest(source, event_origin_local, volume);
    return TRUE;
}

bool leAudioTest_SetVolumeForBroadcast(uint8 volume)
{
    bool status = FALSE;
    audio_source_t focused_source;

    if (leAudioTest_IsLeAudioBroadcastAudioInFocus(&focused_source))
    {
        status = leAudioTest_SetVolumeForLeaAudioSource(focused_source, volume);
    }

    DEBUG_LOG_ALWAYS("leAudioTest_SetVolumeForBroadcast volume %d, status %d", volume, status);
    return status;
}

bool leAudioTest_SetVolumeForUnicastMusic(uint8 volume)
{
    bool status = FALSE;
    audio_source_t focused_source;

    if (leAudioTest_IsLeAudioUnicastAudioInFocus(&focused_source))
    {
        status = leAudioTest_SetVolumeForLeaAudioSource(focused_source, volume);
    }

    DEBUG_LOG_ALWAYS("leAudioTest_SetVolumeForUnicastMusic volume %d, status %d", volume, status);
    return status;
}

static bool leAudioTest_SetMuteForLeaAudioSource(audio_source_t source, bool mute_state)
{
#ifdef INCLUDE_TWS
    if (TwsTopology_IsRoleSecondary())
    {
        DEBUG_LOG_ALWAYS("This Test API should never be called on the secondary Earbud");
        return FALSE;
    }
#endif

    Volume_SendAudioSourceMuteRequest(source, event_origin_local, mute_state);
    return TRUE;
}

bool leAudioTest_SetMuteForBroadcast(bool mute_state)
{
    bool status = FALSE;
    audio_source_t focused_source;

    if (leAudioTest_IsLeAudioBroadcastAudioInFocus(&focused_source))
    {
        status = leAudioTest_SetMuteForLeaAudioSource(focused_source, mute_state);
    }

    DEBUG_LOG_ALWAYS("leAudioTest_SetMuteForBroadcast mute_state %d, status %d", mute_state, status);
    return status;
}

bool leAudioTest_SetMuteForUnicastMusic(bool mute_state)
{
    bool status = FALSE;
    audio_source_t focused_source;

    if (leAudioTest_IsLeAudioUnicastAudioInFocus(&focused_source))
    {
        status = leAudioTest_SetMuteForLeaAudioSource(focused_source, mute_state);
    }

    DEBUG_LOG_ALWAYS("leAudioTest_SetMuteForUnicastMusic mute_state %d, status %d", mute_state, status);
    return status;
}

bool leAudioTest_PauseBroadcast(void)
{
#ifdef INCLUDE_LE_AUDIO_BROADCAST
    DEBUG_LOG_ALWAYS("leAudioTest_PauseBroadcast");
    LeBroadcastManager_Pause(&le_audio_test_taskdata);
    return TRUE;
#else
    return FALSE;
#endif
}

bool leAudioTest_ResumeBroadcast(void)
{
#ifdef INCLUDE_LE_AUDIO_BROADCAST
    DEBUG_LOG_ALWAYS("leAudioTest_ResumeBroadcast");
    LeBroadcastManager_Resume(&le_audio_test_taskdata);
    return TRUE;
#else
    return FALSE;
#endif
}

bool leAudioTest_IsBroadcastPaused(void)
{
#ifdef INCLUDE_LE_AUDIO_BROADCAST
    bool paused = LeBroadcastManager_IsPaused();
    DEBUG_LOG_ALWAYS("leAudioTest_IsBroadcastPaused %d", paused);
    return paused;
#else
    return TRUE;
#endif
}

bool leAudioTest_SetVolumeForUnicastVoice(uint8 volume)
{
    voice_source_t focused_source = voice_source_none;
    bool status = TRUE;

#ifdef INCLUDE_TWS
    if (TwsTopology_IsRoleSecondary())
    {
        DEBUG_LOG_ALWAYS("This Test API should never be called on the secondary Earbud");
        status = FALSE;
    }
#endif

    if (status && leAudioTest_IsLeAudioUnicastVoiceInFocus(&focused_source))
    {
        Volume_SendVoiceSourceVolumeUpdateRequest(focused_source, event_origin_local, volume);
    }
    else
    {
        status = FALSE;
    }

    DEBUG_LOG_ALWAYS("leAudioTest_SetVolumeForUnicastVoice volume: %d focused_source:enum:voice_source_t:%d status: %d",
                     volume, focused_source, status);

    return status;
}

bool leAudioTest_SetMuteForUnicastVoice(bool mute_state)
{
    voice_source_t focused_source = voice_source_none;
    bool status = TRUE;

#ifdef INCLUDE_TWS
    if (TwsTopology_IsRoleSecondary())
    {
        DEBUG_LOG_ALWAYS("This Test API should never be called on the secondary Earbud");
        status = FALSE;
    }
#endif

    if (status && leAudioTest_IsLeAudioUnicastVoiceInFocus(&focused_source))
    {
        Volume_SendVoiceSourceMuteRequest(focused_source, event_origin_local, mute_state);
    }
    else
    {
        status = FALSE;
    }

    DEBUG_LOG_ALWAYS("leAudioTest_SetMuteForUnicastVoice mute_state: %d focused_source:enum:voice_source_t:%d status: %d",
                     mute_state, focused_source, status);

    return status;
}

int leAudioTest_GetVolumeForBroadcast(void)
{
    int volume = 0;
    audio_source_t focused_source;

    if (leAudioTest_IsLeAudioBroadcastAudioInFocus(&focused_source))
    {
        volume = AudioSources_GetVolume(focused_source).value;
    }

    DEBUG_LOG_ALWAYS("leAudioTest_GetVolumeForBroadcast source %d, volume:%d", focused_source, volume);
    return volume;
}

int leAudioTest_GetVolumeForUnicastMusic(void)
{
    int volume = 0;
    audio_source_t focused_source;

    if (leAudioTest_IsLeAudioUnicastAudioInFocus(&focused_source))
    {
        volume = AudioSources_GetVolume(focused_source).value;
    }

    DEBUG_LOG_ALWAYS("leAudioTest_GetVolumeForUnicastMusic source %d, volume:%d", focused_source, volume);
    return volume;
}

int leAudioTest_GetVolumeForUnicastVoice(void)
{
    int volume = 0;
    voice_source_t focused_source;

    if (leAudioTest_IsLeAudioUnicastVoiceInFocus(&focused_source))
    {
        volume = VoiceSources_GetVolume(focused_source).value;
    }

    DEBUG_LOG_ALWAYS("leAudioTest_GetVolumeForUnicastVoice source %d, volume:%d", focused_source, volume);
    return volume;
}

#ifdef INCLUDE_LE_AUDIO_BROADCAST
int16 leAudioTest_ReadBisRssi(broadcast_manager_bis_location_t bis_loc)
{
    return LeBroadcastManager_SourceGetBisRssi(bis_loc);
}
#endif

#ifdef INCLUDE_LE_AUDIO_BROADCAST_SOURCE
static leAudioTest_SetPADataCfmCallback SetPADataCfmHandler = NULL;

static void leAudioTest_SendSetPaDataConfirmation(le_audio_test_set_pa_data_status cfm_status)
{
    uint16 time_to_sync_point_ms = 0;
    uint32 time_pa_anchor_point_us = 0;
    uint32 time_pa_sync_point_us = 0;

    if(cfm_status == le_audio_test_set_pa_data_success)
    {
        time_pa_anchor_point_us = LeAudioTestPaGetTaskData()->time_pa_anchor_point_us;
        time_pa_sync_point_us = LeAudioTestPaGetTaskData()->time_pa_sync_point_us;
        time_to_sync_point_ms = LeAudioTestPaGetTaskData()->time_to_sync_point_ms;
    }

    if (SetPADataCfmHandler != NULL)
    {
        SetPADataCfmHandler(cfm_status, time_to_sync_point_ms, time_pa_anchor_point_us, time_pa_sync_point_us);
    }
}

CapClientBrcstSrcBaseInfo* leAudioTest_GetBaseData(void)
{
    return LeAudioClientBroadcast_GetBaseData();
}

void leAudioTest_SetPAData(uint8 adv_handle,
                           uint8 *adv_data,
                           uint16 adv_data_length,
                           uint32 anchor_point_us,
                           uint16 time_to_sync_point_ms,
                           uint16 offset_for_time_info)
{
    LeAudioClient_ClientRegister(&le_audio_test_set_pa_taskdata);
    LeAudioClientBroadcast_SetPAData(adv_handle,
                                     adv_data,
                                     adv_data_length,
                                     anchor_point_us,
                                     time_to_sync_point_ms,
                                     offset_for_time_info);
}

void leAudioTest_SendManufacturerSpecificDataInPA(uint8 *ms_data,
                                                  uint8 ms_data_length,
                                                  uint16 time_to_sync_point_ms,
                                                  uint16 offset_for_time_info,
                                                  bool reset_anchor_point)
{
    uint16 adv_data_length = 0;
    uint8 base_data_length = 0;

    /* Check if there is already adv_data still being updated and process the request */
    if (adv_data == NULL)
    {
        /* Get the broadcast source base info for the already enabled PA */
        CapClientBrcstSrcBaseInfo* broadcast_source_base_data = leAudioTest_GetBaseData();
        base_data_length = broadcast_source_base_data->baseData[0] + 1;
        adv_data_length = base_data_length + ms_data_length;
        offset_for_time_info += base_data_length;

        DEBUG_LOG("leAudioTest_SendManufacturerSpecificDataInPA, base_data_length = %d, ms_data_length = %d,"
                  "adv_data_length = %d, time_to_sync_point_ms = %d, offset_for_time_info = %d,"
                  "reset_anchor_point = %d", base_data_length, ms_data_length, adv_data_length, time_to_sync_point_ms,
                  offset_for_time_info, reset_anchor_point);
        /* Note: As per the spec, if the PA data is updated when the PA is already enabled, then adv data size can be
         * of LE_AUDIO_TEST_PERIODIC_ADV_DATA_SIZE_MAX(1 fragment), as only 0x03(Complete PA data in single fragment)
         * & 0x04(Unchanged data, only update of adv DID) operations are supported while updating the PA data without
         * disabling the already enabled PA */
        PanicFalse(adv_data_length <= LE_AUDIO_TEST_PERIODIC_ADV_DATA_SIZE_MAX);

        /* Note: Memory allocated to the adv_data which consists of base info obtained from the enabled PA and
         * the manufacturer specific data should be freed and point to NULL only after LE_AUDIO_CLIENT_SET_PA_DATA_CFM
         * is received */
        uint32 time_now = VmGetTimerTime();
        LeAudioTestPaGetTaskData()->time_pa_sync_point_us = time_now + (time_to_sync_point_ms * US_PER_MS);
        LeAudioTestPaGetTaskData()->time_to_sync_point_ms =  time_to_sync_point_ms;
        if (reset_anchor_point || (LeAudioTestPaGetTaskData()->time_pa_anchor_point_us == 0))
        {
            LeAudioTestPaGetTaskData()->time_pa_anchor_point_us = time_now;
        }
        if ((time_now - LeAudioTestPaGetTaskData()->time_pa_anchor_point_us) >
                (TIME_FROM_ANCHOR_POINT_MAX_MS*US_PER_MS))
        {
            DEBUG_LOG("leAudioTest_SendManufacturerSpecificDataInPA, not sending PA REQ as anchor point max reached");
            leAudioTest_SendSetPaDataConfirmation(le_audio_test_set_pa_data_anchor_point_max_reached);
        }
        else if ((LeAudioTestPaGetTaskData()->time_pa_sync_point_us -
                 LeAudioTestPaGetTaskData()->time_pa_anchor_point_us) > (TIME_TO_SYNC_POINT_MAX_MS*US_PER_MS))
        {
            DEBUG_LOG("leAudioTest_SendManufacturerSpecificDataInPA, not sending Set PA Data Req as sync point is more"
                      "than 65s from anchor point");
            leAudioTest_SendSetPaDataConfirmation(le_audio_test_set_pa_data_sync_point_max_reached);
        }
        else
        {
            adv_data = (uint8*) PanicUnlessMalloc((adv_data_length) * sizeof(uint8));
            memcpy(adv_data, broadcast_source_base_data->baseData, base_data_length * sizeof(uint8));
            memcpy(&adv_data[base_data_length], ms_data, ms_data_length * sizeof(uint8));
            uint16 time_from_anchor_to_sync_point_ms =
                    (time_now - LeAudioTestPaGetTaskData()->time_pa_anchor_point_us +
                     (time_to_sync_point_ms * US_PER_MS)) / US_PER_MS;
            DEBUG_LOG("leAudioTest_SendManufacturerSpecificDataInPA, time_pa_data_req=%d, pa_anchor_point=%d,"
                      "reset_anchor_point=%d, time_from_anchor_to_sync_point_ms=%d, time_pa_sync_point_us=%d",
                       time_from_anchor_to_sync_point_ms, LeAudioTestPaGetTaskData()->time_pa_anchor_point_us,
                       reset_anchor_point, LeAudioTestPaGetTaskData()->time_to_sync_point_ms,
                       LeAudioTestPaGetTaskData()->time_pa_sync_point_us);
            /* Set PA data along with time info */
            leAudioTest_SetPAData(broadcast_source_base_data->advHandle,
                                  adv_data,
                                  adv_data_length,
                                  LeAudioTestPaGetTaskData()->time_pa_anchor_point_us,
                                  time_from_anchor_to_sync_point_ms,
                                  offset_for_time_info);

            /* Free the broadcast source base info obtained for the already enabled PA */
            if (broadcast_source_base_data != NULL)
            {
                if (broadcast_source_base_data->baseData != NULL)
                {
                    free(broadcast_source_base_data->baseData);
                }
                free(broadcast_source_base_data);
            }
        }
    }
    else
    {
        DEBUG_LOG("leAudioTest_SendManufacturerSpecificDataInPA, Update of PA data is in progress and "
                  "hence the new request is not processed");
        leAudioTest_SendSetPaDataConfirmation(le_audio_test_set_pa_data_in_progress);
    }
}

void leAudioTest_RegisterSetPADataCfmCallback(leAudioTest_SetPADataCfmCallback callback)
{
    SetPADataCfmHandler = callback;
}

static void leAudioTest_ClearPAData(uint8 **adv_data)
{
    if (*adv_data != NULL)
    {
        free(*adv_data);
        *adv_data = NULL;
    }
}

static void leAudioTest_setPATaskHandler(Task task, MessageId id, Message msg)
{
    UNUSED(task);
    DEBUG_LOG_ALWAYS("leAudioTest_setPATaskHandler id enum:le_audio_client_msg_t:%u", id);

    switch (id)
    {
        case LE_AUDIO_CLIENT_SET_PA_DATA_CFM:
        {
            const LE_AUDIO_CLIENT_SET_PA_DATA_CFM_T *set_pa_data_cfm = msg;
            DEBUG_LOG_ALWAYS("leAudioTest_setPATaskHandler: LE_AUDIO_CLIENT_SET_PA_DATA_CFM, status = %d,"
                             "pa_anchor_point = %d, time_to_sync_point = %d", set_pa_data_cfm->status,
                             LeAudioTestPaGetTaskData()->time_pa_anchor_point_us,
                             LeAudioTestPaGetTaskData()->time_pa_sync_point_us);
            switch (set_pa_data_cfm->status)
            {
                case HCI_SUCCESS:
                case HCI_ERROR_ILLEGAL_FORMAT:
                {
                    /* Free the adv data when LE_AUDIO_CLIENT_SET_PA_DATA_CFM(with status SUCCESS) is received
                     * after sending the entire PA data or even in cases of certain errors in sending Set PA Data
                     * request from app which in turn allows to send next Set PA Data requests */
                    le_audio_test_set_pa_data_status set_pa_data_status = (set_pa_data_cfm->status == HCI_SUCCESS) ?
                                le_audio_test_set_pa_data_success : le_audio_test_pa_set_data_hci_error_illegal_format;
                    leAudioTest_ClearPAData(&adv_data);
                    leAudioTest_SendSetPaDataConfirmation(set_pa_data_status);
                }
                break;

                default:
                {
                    /* Handle the error case if required */
                    leAudioTest_SendSetPaDataConfirmation(le_audio_test_set_pa_data_hci_error);
                }
                break;
            }
            /* Unregister for receiving notifications from LE Audio Client.
             * If new PA update req is sent then it will register again */
            LeAudioClient_ClientUnregister(&le_audio_test_set_pa_taskdata);
        }
        break;

        default:
            DEBUG_LOG_WARN("leAudioTest_setPATaskHandler unexpected id MESSAGE:0x%x", id);
        break;
    }
}
#endif /* INCLUDE_LE_AUDIO_BROADCAST_SOURCE */

#ifdef INCLUDE_LE_AUDIO_UNICAST

/*! Increment/decrement the volume of unicast audio sink */
static void leAudioTest_UnicastAudioSinkVolumeChange(bool is_vol_down)
{
    int step_size;
    int step_mul = is_vol_down ? -1 : 1;
    audio_source_t focused_source;

    if (leAudioTest_IsLeAudioUnicastAudioInFocus(&focused_source))
    {
        step_size = VolumeService_GetUiStepSize(AudioSources_GetVolume(focused_source).config) * step_mul;
        VolumeService_ChangeAudioSourceVolume(focused_source, step_size);
    }
}

/*! Increment/decrement the volume of unicast voice sink */
static void leAudioTest_UnicastVoiceSinkVolumeChange(bool is_vol_down)
{
    int step_size;
    int step_mul = is_vol_down ? -1 : 1;
    voice_source_t focused_source;

    if (leAudioTest_IsLeAudioUnicastVoiceInFocus(&focused_source))
    {
        step_size = VolumeService_GetUiStepSize(VoiceSources_GetVolume(focused_source).config) * step_mul;
        VolumeService_ChangeVoiceSourceVolume(focused_source, step_size);
    }
}

void leAudioTest_UnicastAudioSinkVolumeUp(void)
{
    DEBUG_LOG_ALWAYS("leAudioTest_UnicastAudioSinkVolumeUp");

    leAudioTest_UnicastAudioSinkVolumeChange(FALSE);
}

void leAudioTest_UnicastAudioSinkVolumeDown(void)
{
    DEBUG_LOG_ALWAYS("leAudioTest_UnicastAudioSinkVolumeDown");

    leAudioTest_UnicastAudioSinkVolumeChange(TRUE);
}

void leAudioTest_UnicastVoiceSinkVolumeUp(void)
{
    DEBUG_LOG_ALWAYS("leAudioTest_UnicastVoiceSinkVolumeUp");

    leAudioTest_UnicastVoiceSinkVolumeChange(FALSE);
}

void leAudioTest_UnicastVoiceSinkVolumeDown(void)
{
    DEBUG_LOG_ALWAYS("leAudioTest_UnicastVoiceSinkVolumeDown");

    leAudioTest_UnicastVoiceSinkVolumeChange(TRUE);
}

void leAudioTest_SendAseReleaseRequest(gatt_cid_t cid, uint8 ase_id)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SendAseReleaseRequest");
    LeUnicastManager_SendAseReleaseRequest(cid, ase_id);
}

void leAudioTest_SendAseReleasedRequest(gatt_cid_t cid, uint8 ase_id)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SendAseReleasedRequest");
    LeUnicastManager_SendAseReleasedRequest(cid, ase_id);
}

void leAudioTest_SendAseDisableRequest(gatt_cid_t cid, uint8 ase_id)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SendAseDisableRequest");
    LeUnicastManager_SendAseDisableRequest(cid, ase_id);
}

void leAudioTest_SendAseAseConfigureCodecReq(gatt_cid_t cid, uint8 ase_id, le_um_codec_config_set_t config_set)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SendAseAseConfigureCodecReq");
    LeUnicastManager_SendAseConfigureCodecReq(cid, ase_id, config_set);
}

void leAudioTest_SendAseMetadataUpdateReq(gatt_cid_t cid, uint8 ase_id)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SendAseMetadataUpdateReq");
    LeUnicastManager_SendAseMetadataUpdate(cid, ase_id);
}

bool leAudioTest_IsCcpConnected(void)
{
    bool is_connected = CallClientControl_IsCcpConnected();
    DEBUG_LOG_ALWAYS("leAudioTest_IsCcpConnected %d", is_connected);

    return is_connected;
}

bool appTestIsLeaUnicastStreaming(void)
{
    bool is_streaming = LeUnicastManager_IsStreamingActive();
    DEBUG_LOG_ALWAYS("appTestIsLeaUnicastStreaming %d", is_streaming);
    return is_streaming;
}

uint16 appTestLeaUnicastGetAudioContext(void)
{
    uint16 audio_context = LeUnicastManager_GetUnicastAudioContext();

    DEBUG_LOG_ALWAYS("appTestIsLeaUnicastGetAudioContext audio_context=enum:audio_context_t:%d", audio_context);
    return audio_context;
}

bool leAudioTest_ReadUnicastCisLinkQuality(void)
{
    hci_connection_handle_t handles[2] = {LE_INVALID_CIS_HANDLE};

#ifdef ENABLE_LEA_CIS_DELEGATION
    uint8 own_dir, peer_dir;

    MirrorProfile_GetCisHandle(&handles[0], &handles[1], &own_dir, &peer_dir);

    DEBUG_LOG_ALWAYS("leAudioTest_ReadUnicastCisLinkQuality own-handle: 0x%x, peer-handle: 0x%x", handles[0], handles[1]);
#else
    DEBUG_LOG_ALWAYS("leAudioTest_ReadUnicastCisLinkQuality total handles: %d, handle-1: 0x%x, handle-2: 0x%x",
                     LeUnicastManager_GetCisHandles(ARRAY_DIM(handles), handles), handles[0], handles[1]);
#endif

    if (handles[0] != LE_INVALID_CIS_HANDLE)
    {
        CmIsocReadIsoLinkQualityReq(&le_audio_test_taskdata, handles[0]);
    }

    if (handles[1] != LE_INVALID_CIS_HANDLE)
    {
        CmIsocReadIsoLinkQualityReq(&le_audio_test_taskdata, handles[1]);
    }

    return handles[0] != LE_INVALID_CIS_HANDLE || handles[1] != LE_INVALID_CIS_HANDLE;
}

bool leAudioTest_LeUnicastRemoveDataPaths(hci_connection_handle_t cis_handle)
{
    bool status = LeUnicastManager_RemoveDataPathByCisHandle(cis_handle);

    DEBUG_LOG_ALWAYS("leAudioTest_LeUnicastRemoveDataPaths: cis_handle=0x%04x, status: %d", cis_handle, status);

    return status;
}

#ifdef ENABLE_LE_AUDIO_FT_UPDATE

UNITCODESECTION(KEEP)
void leAudioTest_UpdateVSFTMetadata(uint8_t min_flush_timeout,uint8_t max_flush_timeout, uint8_t max_bit_rate,
                                    uint8_t err_resilience, uint8_t latency_and_downmix)
{
    le_um_ft_info_t ft_info;
    ft_info.min_flush_timeout = min_flush_timeout;
    ft_info.max_flush_timeout = max_flush_timeout;
    ft_info.max_bit_rate = max_bit_rate;
    ft_info.err_resilience = err_resilience;
    ft_info.latency_mode = latency_and_downmix;
    LeUnicastManager_UpdateVSFTMetadata(&ft_info);
}

#endif /* ENABLE_LE_AUDIO_FT_UPDATE */

#endif /* INCLUDE_LE_AUDIO_UNICAST */

bool leAudioTest_AnyHandsetConnectedBothBredrAndLe(void)
{
    cm_connection_t *le_connection;

    le_connection = ConManagerFindFirstActiveLink(cm_transport_ble);
    while (le_connection)
    {
        const tp_bdaddr *address = ConManagerGetConnectionTpAddr(le_connection);
        if (address)
        {
            tp_bdaddr le_address = {0};

            if (ConManagerResolveTpaddr(address, &le_address))
            {
                tp_bdaddr bredr_address = le_address;
                bredr_address.transport = TRANSPORT_BREDR_ACL;
                if (ConManagerFindConnectionFromBdAddr(&bredr_address))
                {
                    DEBUG_LOG_ALWAYS("leAudioTest_AnyHandsetConnectedBothBredrAndLe. Found device with LE and BREDR addr:(0x%6lx)",
                                le_address.taddr.addr.lap);
                    return TRUE;
                }
            }
        }
        le_connection = ConManagerFindNextActiveLink(le_connection, cm_transport_ble);
    }

    DEBUG_LOG_ALWAYS("leAudioTest_AnyHandsetConnectedBothBredrAndLe. No devices.");

    return FALSE;
}

#if defined(INCLUDE_LE_AUDIO_BROADCAST) || defined(INCLUDE_LE_AUDIO_UNICAST)
static void leAudioTest_taskHandler(Task task, MessageId id, Message msg)
{
    UNUSED(task);

#ifdef INCLUDE_LE_AUDIO_UNICAST

    if (id == CM_PRIM && (*(CsrBtCmPrim *) msg) == CM_ISOC_READ_ISO_LINK_QUALITY_CFM)
    {
        CmIsocReadIsoLinkQualityCfm *cfm = (CmIsocReadIsoLinkQualityCfm *) msg;

        DEBUG_LOG_ALWAYS("leAudioTest_taskHandler::CM_ISOC_READ_ISO_LINK_QUALITY_CFM handle: 0x%x, result: {code: 0x%x, supplier: 0x%x}",
                         cfm->handle, cfm->resultCode, cfm->resultSupplier);
        DEBUG_LOG_ALWAYS("     tx_unacked_packets: 0x%x, tx_flushed_packets: 0x%x, tx_last_subevent_packets: 0x%x, retransmitted_packets: 0x%x",
                         cfm->tx_unacked_packets, cfm->tx_flushed_packets, cfm->tx_last_subevent_packets, cfm->retransmitted_packets);
        DEBUG_LOG_ALWAYS("     crc_error_packets: 0x%x, rx_unreceived_packets: 0x%x, duplicate_packets: 0x%x",
                         cfm->crc_error_packets, cfm->rx_unreceived_packets, cfm->duplicate_packets);
    }

#else /* INCLUDE_LE_AUDIO_UNICAST */

    UNUSED(id);
    UNUSED(msg);

#endif /* INCLUDE_LE_AUDIO_UNICAST */

#ifdef INCLUDE_LE_AUDIO_BROADCAST
    if (id == LE_AUDIO_BROADCAST_METADATA_PAYLOAD)
    {
        LE_AUDIO_BROADCAST_METADATA_PAYLOAD_T *message = (LE_AUDIO_BROADCAST_METADATA_PAYLOAD_T *) msg;

        DEBUG_LOG_ALWAYS("leAudioTest_taskHandler: LE_AUDIO_BROADCAST_METADATA_PAYLOAD metadata_len: 0x%x, ltv_1_len: 0x%x, ltv_1_type: 0x%x",
                         message->metadata_len, message->metadata[0], message->metadata[1]);

        le_audio_test_pa_data_len = message->metadata_len;
        if (le_audio_test_pa_data_len <= LE_AUDIO_TEST_PA_DATA_MAX_SIZE)
        {
            memcpy(le_audio_test_pa_data, message->metadata, message->metadata_len);
        }
    }
#endif /* INCLUDE_LE_AUDIO_BROADCAST */

#ifdef ENABLE_ACK_FOR_PA_TRANSMITTED
    if (id == LE_AUDIO_CLIENT_PA_TRANSMITTED_IND)
    {
        DEBUG_LOG_ALWAYS("leAudioTest_taskHandler: LE_AUDIO_CLIENT_PA_TRANSMITTED_IND");
        /* unregister, if new metadata is sent then it will register again */
        LeAudioClient_ClientUnregister(&le_audio_test_taskdata);
    }
#endif /* ENABLE_ACK_FOR_PA_TRANSMITTED */

}

#ifdef INCLUDE_LE_AUDIO_BROADCAST

#include "broadcast_sink_role.h"

uint16 leAudioTest_GetPastTimeout(void)
{
    uint16 timeout = LeBroadcastManager_GetPastTimeoutMs();

    DEBUG_LOG_ALWAYS("leAudioTest_GetPastTimeout timeout %ums", timeout);

    return timeout;
}

void leAudioTest_SetPastTimeout(uint16 timeout)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SetPastTimeout timeout %ums", timeout);

    LeBroadcastManager_SetPastTimeoutMs(timeout);
}

uint16 leAudioTest_GetFindTrainsTimeout(void)
{
    uint16 timeout = LeBroadcastManager_GetFindTrainsTimeout();

    DEBUG_LOG_ALWAYS("leAudioTest_GetFindTrainsTimeout timeout %ums", timeout);

    return timeout;
}

void leAudioTest_SetFindTrainsTimeout(uint16 timeout)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SetFindTrainsTimeout timeout %ums", timeout);

    LeBroadcastManager_SetFindTrainsTimeout(timeout);
}

uint16 leAudioTest_GetSyncToTrainTimeout(void)
{
    uint16 timeout = LeBapBroadcastSink_GetSyncToTrainTimeout();

    DEBUG_LOG_ALWAYS("leAudioTest_GetSyncToTrainTimeout timeout %ums", timeout);

    return timeout;
}

void leAudioTest_SetSyncToTrainTimeout(uint16 timeout)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SetSyncToTrainTimeout timeout %ums", timeout);

    LeBapBroadcastSink_SetSyncToTrainTimeout(timeout);
}

void leAudioTest_EnableBroadcastMetadataNotification(bool enable)
{
    DEBUG_LOG_ALWAYS("leAudioTest_EnableMetadataTransaction %d", enable);

    LeBroadcastManager_EnableMetadataNotification(enable);
    enable ? LeAudioMessages_ClientRegister(&le_audio_test_taskdata)
           : LeAudioMessages_ClientDeregister(&le_audio_test_taskdata);
}

#ifdef INCLUDE_LE_AUDIO_BROADCAST_SOURCE

/* Expected broadcast reciever as well as broadcast transmitter enabled to verify this functionality */
bool leAudioTest_UpdateBroadcastMetadata(void)
{
    bool status = FALSE;

    if (le_audio_test_pa_data_len > 0 && le_audio_test_pa_data_len <= LE_AUDIO_TEST_PA_DATA_MAX_SIZE)
    {
        status = LeAudioClientBroadcast_UpdateMetadataInPeriodicTrain(le_audio_test_pa_data_len, le_audio_test_pa_data);

#ifdef ENABLE_ACK_FOR_PA_TRANSMITTED
        LeAudioClient_ClientRegister(&le_audio_test_taskdata);
#endif /* ENABLE_ACK_FOR_PA_TRANSMITTED */
    }

    DEBUG_LOG_ALWAYS("leAudioTest_UpdateBroadcastMetadata metadata_len: 0x%x, ltv_1_len: 0x%x, ltv_1_type: 0x%x, status: %u",
                     le_audio_test_pa_data_len, le_audio_test_pa_data[0], le_audio_test_pa_data[1], status);

    return status;
}

#endif /* INCLUDE_LE_AUDIO_BROADCAST_SOURCE */

uint8 leAudioTest_BassAddSource(uint8 advertising_sid, uint32 broadcast_id, le_bm_pa_sync_t pa_sync)
{
    uint8 source_id = 0;

#if defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN)

    /* Minimal broadcast subgroup array; a single subgroup with no preferred BIS index and no metadata */
    le_bm_source_subgroup_t subgroups[] =
    {
        {
            .bis_sync = BIS_SYNC_NO_PREFERENCE
        }
    };

    le_bm_add_source_info_t add_source_info =
    {
        .advertising_sid = advertising_sid,
        .broadcast_id = broadcast_id,
        .pa_sync = pa_sync,
        .pa_interval = LE_BM_PA_INTERVAL_UNKNOWN,
        .num_subgroups = ARRAY_DIM(subgroups),
        .subgroups = subgroups
    };

    appDeviceGetMyBdAddr(&add_source_info.assistant_address.addr);
    LeBroadcastManager_BassAddSource(&source_id, &add_source_info);

    DEBUG_LOG_ALWAYS("leAudioTest_BassAddSource: source_id=%u", source_id);

#else
    UNUSED(advertising_sid);
    UNUSED(broadcast_id);
    UNUSED(pa_sync);

    DEBUG_LOG_ALWAYS("leAudioTest_BassAddSource: Feature Not Enabled");
#endif /* defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN) */

    return source_id;
}

le_bm_bass_status_t leAudioTest_BassModifySource(uint8 source_id, le_bm_pa_sync_t pa_sync)
{
    le_bm_bass_status_t result = le_bm_bass_status_failed;

#if defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN)
    /* Minimal broadcast subgroup array; a single subgroup with no preferred BIS index and no metadata */
    le_bm_source_subgroup_t subgroups[] =
    {
        {
            .bis_sync = BIS_SYNC_NO_PREFERENCE
        }
    };

    le_bm_modify_source_info_t modify_source_info =
    {
        .pa_sync = pa_sync,
        .pa_interval = LE_BM_PA_INTERVAL_UNKNOWN,
    };

    if (pa_sync != le_bm_pa_sync_none)
    {
        modify_source_info.num_subgroups = ARRAY_DIM(subgroups);
        modify_source_info.subgroups = subgroups;
    }

    result = LeBroadcastManager_BassModifySource(source_id, &modify_source_info);
    DEBUG_LOG_ALWAYS("leAudioTest_BassModifySource: source_id=%u pa_sync=enum:le_bm_pa_sync_t:%d result=enum:le_bm_bass_status_t:%d", source_id, pa_sync, result);
#else
    UNUSED(source_id);
    UNUSED(pa_sync);

    DEBUG_LOG_ALWAYS("leAudioTest_BassModifySource: feature not enabled");
#endif /* INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN */

    return result;
}

le_bm_bass_status_t leAudioTest_BassSetBroadcastCode(uint8 source_id, const char *code_string)
{
    le_bm_bass_status_t result = le_bm_bass_status_failed;

#if defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN)
    size_t code_len = strlen(code_string);

    if (code_len <= SCAN_DELEGATOR_BROADCAST_CODE_SIZE)
    {
        uint8 *broadcast_code = PanicNull(calloc(1, SCAN_DELEGATOR_BROADCAST_CODE_SIZE));

        memcpy(broadcast_code, code_string, code_len);
        result = LeBroadcastManager_BassSetBroadcastCode(source_id, broadcast_code);

        DEBUG_LOG_ALWAYS("leAudioTest_BassSetBroadcastCode: result=enum:le_bm_bass_status_t:%d", result);
        DEBUG_LOG_DATA_VERBOSE(broadcast_code, SCAN_DELEGATOR_BROADCAST_CODE_SIZE);

        free(broadcast_code);
    }
    else
    {
        DEBUG_LOG_ALWAYS("leAudioTest_BassSetBroadcastCode: code string too long (%u > %u)", code_len, SCAN_DELEGATOR_BROADCAST_CODE_SIZE);
    }

#else
    UNUSED(source_id);
    UNUSED(code_string);

    DEBUG_LOG_ALWAYS("leAudioTest_BassSetBroadcastCode: feature not enabled");
#endif /* INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN */

    return result;
}

le_bm_bass_status_t leAudioTest_BassRemoveSource(uint8 source_id)
{
    le_bm_bass_status_t result = le_bm_bass_status_failed;

#if defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN)
    result = LeBroadcastManager_BassRemoveSource(source_id);

    DEBUG_LOG_ALWAYS("leAudioTest_BassRemoveSource: source_id=%u result=enum:le_bm_bass_status_t:%d", source_id, result);
#else
    UNUSED(source_id);

    DEBUG_LOG_ALWAYS("leAudioTest_BassRemoveSource: feature not enabled");
#endif /* INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN */

    return result;
}

#if defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN)
static void leAudioTest_SelfScanHandleCmExtendedScannerGlobalParamsCfm(CmExtScanGetGlobalParamsCfm *cfm)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SelfScanHandleCmExtendedScannerGlobalParamsCfm Result 0x%x", cfm->resultCode);

    if (cfm->resultCode == CSR_BT_RESULT_CODE_CM_SUCCESS)
    {
        DEBUG_LOG_ALWAYS("  flags 0x%x own_addr_type 0x%x scanning_filter_policy 0x%x",
                         cfm->flags, cfm->own_address_type, cfm->scanning_filter_policy);
        DEBUG_LOG_ALWAYS("  filter_duplicates 0x%x scanning_phys %u ",
                         cfm->filter_duplicates, cfm->scanning_phys);

        for (uint32 i = 0; i < cfm->scanning_phys; i++)
        {
            CmScanningPhy *phy = &cfm->phys[i];

            DEBUG_LOG_ALWAYS("    scan_type 0x%x scan_interval 0x%x scan_window 0x%x",
                             phy->scan_type, phy->scan_interval, phy->scan_window);
        }
    }

}

static bool leAudioTest_SelfScanHandleExtendedCmMessages(void *msg)
{
    CsrBtCmPrim *primType = (CsrBtCmPrim *) msg;
    bool status = TRUE;

    switch (*primType)
    {
        case CSR_BT_CM_EXT_SCAN_GET_GLOBAL_PARAMS_CFM:
            leAudioTest_SelfScanHandleCmExtendedScannerGlobalParamsCfm((CmExtScanGetGlobalParamsCfm *) msg);
        break;

        default:
        {
            status = FALSE;
        }
        break;
    }

    CmFreeUpstreamMessageContents((void *) msg);

    return status;
}


static void leAudioTest_SelfScanHandler(Task task, MessageId id, Message msg)
{
    UNUSED(task);

    DEBUG_LOG_ALWAYS("leAudioTest_SelfScanHandler id enum:le_broadcast_manager_self_scan_msg_t:%u", id);

    switch (id)
    {
    case LE_BROADCAST_MANAGER_SELF_SCAN_START_CFM:
        {
            LE_BROADCAST_MANAGER_SELF_SCAN_START_CFM_T *cfm = (LE_BROADCAST_MANAGER_SELF_SCAN_START_CFM_T *)msg;
            DEBUG_LOG_ALWAYS("  LE_BROADCAST_MANAGER_SELF_SCAN_START_CFM status enum:le_broadcast_manager_self_scan_status_t:%u", cfm->status);
        }
        break;

    case LE_BROADCAST_MANAGER_SELF_SCAN_STOP_CFM:
        {
            LE_BROADCAST_MANAGER_SELF_SCAN_STOP_CFM_T *cfm = (LE_BROADCAST_MANAGER_SELF_SCAN_STOP_CFM_T *)msg;
            DEBUG_LOG_ALWAYS("  LE_BROADCAST_MANAGER_SELF_SCAN_STOP_CFM status enum:le_broadcast_manager_self_scan_status_t:%u", cfm->status);
        }
        break;

    case LE_BROADCAST_MANAGER_SELF_SCAN_STATUS_IND:
        {
            LE_BROADCAST_MANAGER_SELF_SCAN_STATUS_IND_T *ind = (LE_BROADCAST_MANAGER_SELF_SCAN_STATUS_IND_T *)msg;
            DEBUG_LOG_ALWAYS("  LE_BROADCAST_MANAGER_SELF_SCAN_STATUS_IND status enum:le_broadcast_manager_self_scan_status_t:%u", ind->status);
        }
        break;

    case LE_BROADCAST_MANAGER_SELF_SCAN_DISCOVERED_SOURCE_IND:
        {
            LE_BROADCAST_MANAGER_SELF_SCAN_DISCOVERED_SOURCE_IND_T *ind = (LE_BROADCAST_MANAGER_SELF_SCAN_DISCOVERED_SOURCE_IND_T *)msg;
            DEBUG_LOG_ALWAYS("  LE_BROADCAST_MANAGER_SELF_SCAN_DISCOVERED_SOURCE_IND broadcast_id 0x%x ", ind->broadcast_id);
            DEBUG_LOG_ALWAYS("    advertising_sid 0x%x", ind->adv_sid);
            DEBUG_LOG_ALWAYS("    rssi %d", ind->rssi);
            if (ind->broadcast_name)
            {
                DEBUG_LOG_ALWAYS("    broadcast_name");
                DEBUG_LOG_DATA_ALWAYS(ind->broadcast_name, ind->broadcast_name_len);
                if ( ind->rssi > -75)
                {
                    leAudioTest_BassAddSource(ind->adv_sid, ind->broadcast_id, 1);
                }
            }
        }
        break;

#ifdef USE_SYNERGY
    case CM_PRIM:
        leAudioTest_SelfScanHandleExtendedCmMessages((void *)msg);
        break;
#endif

    default:
        DEBUG_LOG_WARN("leAudioTest_SelfScanHandler unexpected id MESSAGE:0x%x", id);
        break;
    }
}

static TaskData le_audio_test_self_scan_task = {
    .handler = leAudioTest_SelfScanHandler
};
#endif /* defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN) */

void leAudioTest_SelfScanStart(uint32 timeout, uint32 broadcast_id)
{
#if defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN)
    DEBUG_LOG_ALWAYS("leAudioTest_SelfScanStart timeout %u broadcast_id 0x%x", timeout, broadcast_id);

    self_scan_params_t scan_params = {
        .timeout = timeout,
        .filter = {
            .broadcast_id = broadcast_id,
        }
    };

    LeBroadcastManager_SelfScanStart(&le_audio_test_self_scan_task, &scan_params);
#else
    UNUSED(timeout);
    UNUSED(broadcast_id);

    DEBUG_LOG_ALWAYS("leAudioTest_SelfScanStart: feature not enabled");
#endif
}

void leAudioTest_SelfScanStop(void)
{
#if defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN)
    DEBUG_LOG_ALWAYS("leAudioTest_SelfScanStop");

    LeBroadcastManager_SelfScanStop(&le_audio_test_self_scan_task);
#else
    DEBUG_LOG_ALWAYS("leAudioTest_SelfScanStop: feature not enabled");
#endif
}

void leAudioTest_SelfScanStopAll(void)
{
#if defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN)
    DEBUG_LOG_ALWAYS("leAudioTest_SelfScanStopAll");

    LeBroadcastManager_SelfScanStopAll(&le_audio_test_self_scan_task);
#else
    DEBUG_LOG_ALWAYS("leAudioTest_SelfScanStopAll: feature not enabled");
#endif
}

le_bm_bass_status_t leAudioTest_BassSetSourceMatchAddress(uint8 source_id, typed_bdaddr *taddr)
{
    le_bm_bass_status_t result = le_bm_bass_status_failed;

#if defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN)
    result = LeBroadcastManager_BassSetSourceMatchAddress(source_id, taddr);

    DEBUG_LOG_ALWAYS("leAudioTest_BassSetSourceMatchAddress: result=enum:le_bm_bass_status_t:%d", result);
#else
    UNUSED(source_id);
    UNUSED(taddr);
    DEBUG_LOG_ALWAYS("leAudioTest_BassSetSourceMatchAddress: feature not enabled");
#endif

    return result;
}

void leAudioTest_GetGlobalLeScanParameters(void)
{
#if defined(INCLUDE_LE_AUDIO_BROADCAST_LOCAL_SCAN)
    DEBUG_LOG_ALWAYS("leAudioTest_GetGlobalLeScanParameters");

    CmExtScanGetGlobalParamsRequest(&le_audio_test_self_scan_task);
#else
    DEBUG_LOG_ALWAYS("leAudioTest_GetGlobalLeScanParameters: feature not enabled");
#endif
}

#endif /* INCLUDE_LE_AUDIO_BROADCAST */

UNITCODESECTION(KEEP)
bool leAudioTest_LeSubrateChangeReq(gatt_cid_t cid,
                                    uint16 subrate_min,
                                    uint16 subrate_max,
                                    uint16 max_latency,
                                    uint16 continuation_number,
                                    uint16 supervision_timeout)
{
    DEBUG_LOG_ALWAYS("leAudioTest_LeSubrateChangeReq subrate_min 0x%x, subrate_max 0x%x", subrate_min, subrate_max);

    return (ConManagerLeSubrateChangeReq(GattConnect_GetHandleFromConnectionId(cid),
                                         subrate_min, subrate_max, max_latency,
                                         continuation_number, supervision_timeout));
}

UNITCODESECTION(KEEP)
bool appTestIsLEAHandsetConnected(void)
{
    bool connected = FALSE;
    tp_bdaddr addr;
    device_t device;
    cm_connection_iterator_t iterator;

    if (ConManager_IterateFirstActiveConnection(&iterator, &addr))
    {
        do
        {
            if (addr.transport == TRANSPORT_BLE_ACL)
            {
                 device = BtDevice_GetDeviceForTpbdaddr(&addr);
                 if (device != NULL && BtDevice_IsProfileSupportedForDevice(device, DEVICE_PROFILE_LE_AUDIO))
                 {
                     connected = TRUE;
                     break;
                 }
            }
        } while (ConManager_IterateNextActiveConnection(&iterator, &addr));
    }

    DEBUG_LOG_ALWAYS("appTestIsLEAHandsetConnected = %d", connected);
    return connected;
}

static kymera_chain_config_callbacks_t le_voice_chain_callbacks = {0};

static void leAudioTest_KymeraVoiceChain_cb(kymera_chain_handle_t chain_handle, kymera_le_voice_config_params_t *params)
{
    UNUSED(chain_handle);
    DEBUG_LOG_ALWAYS("leAudioTest_KymeraVoiceChain_cb speaker_present = %d, mic_present = %d", params->speaker_path_present, params->microphone_path_present);
}

void leAudioTest_UpdatSinkAudioLocationToRender(uint32 audio_locations)
{
    DEBUG_LOG("leAudioTest_UpdatSinkAudioLocationToRender Audio Location to render:0x%x", audio_locations);

    LeBroadcastManager_UpdateSinkAudioLocationToRender(audio_locations);
}

UNITCODESECTION(KEEP)
void leAudioTest_RegisterKymeraLeVoiceChainCallback(void)
{
    Kymera_RegisterConfigCallbacks(&le_voice_chain_callbacks);
    le_voice_chain_callbacks.ConfigureLeVoiceChain = leAudioTest_KymeraVoiceChain_cb;
}

#endif /* defined(INCLUDE_LE_AUDIO_BROADCAST) || defined(INCLUDE_LE_AUDIO_UNICAST) */

